import { mapGetters } from 'vuex'

import baguaElement from '@/templates/bagua/baguaElement/baguaElement.vue'
import baguaRelation from '@/templates/bagua/baguaRelation/baguaRelation.vue'
import locationTip from '@/templates/location/locationTip/locationTip.vue'
import termTag from '@/templates/bagua/termTag/termTag.vue'

import datePopupMixin from '@/mixins/datePopup.js'
import commonMixin from '@/mixins/common.js'

import CM from '@/class/index.js'
import utils from '@/utils/index.js'

// 日历一行的高度
const CALENDAR_LINE_HEIGHT = 60
// 日历头部的高度
const CALENDAR_WEEK_HEIGHT = 22
// 日历的边距高度
const CALENDAR_PADDING = 5
// 日历的边框高度
const CALENDAR_BORDER = 1
// 导航条高度
const SCROLL_BAR_HEIGHT = 40
// 页面滚动动画时间
const SCROLL_DURATION = 150
// 日历滚动动画时间
const CALENDAR_DURATION = 200

// 时间滚动单项宽度，需要和CSS的width保持一致
const HOUR_SCROLLER_ITEMS_WIDTH = 60
// 月相滚动单项宽度，需要和CSS的width保持一致
const MOON_SCROLLER_ITEMS_WIDTH = 70

export default {
  name: 'calendar',
  components: {
    baguaElement,
    baguaRelation,
    termTag,
    locationTip
  },
  mixins: [
    datePopupMixin,
    commonMixin
  ],
  data () {
    return {
      // 无论是statusBar还是customBar都要再重复赋值，避免小程序出问题
      CustomBar: this.CustomBar,
      StatusBar: this.StatusBar,

      // 是否加载页面
      isLoaded: false,

      // 是否显示周历
      isWeekCalendarShow: false,
      
      // 日历滑动时间
      calendarAniDuration: CALENDAR_DURATION,
     
      // 当前显示的时间信息
      date: new CM.Date(null, true),
      // 用于初始化日历的设定
      initOptions: null,
      // 导航栏数据
      navData: [
        {
          text: '日历',
          id: 'calendar'
        },
        {
          text: '基本信息',
          id: 'basic'
        },
        {
          text: '干支',
          id: 'ganzhi'
        },
        {
          text: '气朔',
          id: 'terms'
        },
        {
          text: '气象',
          id: 'sun'
        }
      ],
      // 四柱调整选中项，默认选中时柱
      ganzhiSelectedIndex: 3,
      
      // 输入日期
      inputYear: '',
      inputMonth: '',
      inputDay: '',
      
      // 四柱留意关系
      relations: {},
      // 全年节气表
      yearTerms: [],
      // 日历是否显示月相
      isMoonCalendar: false,
      // 留意关系隐藏模态框内部滚动位置
      // 为了填uni-app的坑，使得scrollTop能够变化
      modalScrollTop: -1,
      
      // 日出日落组件的样式
      sunStyle: {},
      // 日出日落说明组件的样式
      sunDetailStyle: {},
      // 太阳尺寸
      sunSize: 48,
    }
  },
  computed: {
    ...mapGetters({
      selectedLocation: 'location/selectedLocation',
    }),
    
    /* @section 以下都是页面数据 */    
    // 公历全文
    solarDate () {
      return this.date ? this.date.getSolarName() + '' : ''
    },
    
    // 公历时间
    solarTime () {
      return this.date ? this.date.getTime() + '' : ''
    },
    
    // 农历文字 农历X月XX
    lunarText () {
      return this.date ? this.date.getLunarName() : ''
    },
    
    // 每天的所有时干支列表
    allHours () {
      if (this.date) {
        const hours = this.date.getDayHours()
        return hours
      }
      return []
    },
    
    // 时间列表滚动距离
    timeScrollLeft () {      
      if (this.date) {
        const zhiIndex = this.date.ganzhi[3].zhi.index
        return (11 - zhiIndex) * HOUR_SCROLLER_ITEMS_WIDTH
      }
      return 0
    },
    
    // 月相列表滚动距离
    moonScrollLeft () {
      if (this.date && this.date.prevMoon) {
        return (this.date.rulue - this.date.prevMoon.time.rulue) * MOON_SCROLLER_ITEMS_WIDTH
      }
      return 0
    },
    
    // 日出日落时间
    sunState () {
      if (!this.selectedLocation || !this.date) {
        return { rise: '', down: '' }
      }
      return this.date.getSun(this.selectedLocation)
    },
    
    // 日出日落模块太阳的位置
    sunPos () {
      if (!this.date || !this.sunStyle.hasOwnProperty('width')
        || !this.sunState.hasOwnProperty('riseMin') || !this.sunState.hasOwnProperty('downMin')) {
        // 太阳不显示
        return { display: 'none' }
      }
      
      const nowMin = this.date.hour * CM.Date.config.MIN_IN_HOUR + this.date.min
      if (nowMin > this.sunState.downMin) {
        return { display: 'none' }
      }
      
      const percent = (nowMin - this.sunState.riseMin) / (this.sunState.downMin - this.sunState.riseMin)
      const angle = Math.PI - (Math.PI * percent)
      // 去掉px
      const radius = parseFloat(this.sunStyle.width.slice(0, -2)) / 2
      const x = Math.cos(angle) * radius + radius - (this.sunSize / 2)
      const y = Math.sin(angle) * radius - (this.sunSize / 2)
      return {
        bottom: y + 'px',
        left: x + 'px'
      }
    },
    
    /* @section 以下都是样式 */
    // 周历样式
    weekCalendarStyle () {
      return {
        top: this.CustomBar + 'px',
      }
    },
    
    // 导航条吸顶高度
    scrollTop () {
      return this.CustomBar 
        + CALENDAR_LINE_HEIGHT 
        + CALENDAR_WEEK_HEIGHT 
        + CALENDAR_PADDING
        + 2 * CALENDAR_BORDER
    },
  },
  methods: {
    // 初始化
    init (onSuccess, onError) {
      // 获取初始化设定
      this.initOptions = this.date.getSolarOptions()
      // 初始化日期Picker
      this.initDatePicker()
      
      // 显示主界面，结束加载
      this.isLoaded = true
      onSuccess()
      
      this.$nextTick(() => {
        // 初始化导航滚动内容
        this.initPos()
        // 初始化日出日落组件
        this.initSun()
      })
    },
    
    // 日历选中日期发生变化
    dateChangeHandler (date, type) {   
      const options = date.getSolarOptions()
      // 按照万年历的标准详细计算一轮
      this.date = date
      this.date.initExtras()
      console.log(this.date)
      
      if (type === 'month') {
        this.$nextTick(() => {
          this.$refs.weekCalendar.init(options, false)
        })
      }
      else {
        this.$nextTick(() => {
          this.$refs.monthCalendar.init(options, false)
        })
      }
    },
    
    // 点选“今天”触发事件
    todayHandler () {
      const day = new Date()
      const dayOptions = CM.Date.transDate(day)
      this.setDate(dayOptions.year, dayOptions.month, dayOptions.day, dayOptions.hour, dayOptions.min)
    },
    
    // 点击输入时间触发事件
    dateInputHandler () {
      this.inputYear = ''
      this.inputMonth = ''
      this.inputDay = ''
      this.$refs.timeInput.show()
    },
    
    // 点击查看天气触发事件
    weatherHandler () {
      if (!this.selectedLocation) {
        this.$u.msg.toast('没有有效的位置')
        return
      }
      let url = this.$u.config.sys.weathers.h5Path
      const lc = this.selectedLocation.lon + ',' + this.selectedLocation.lat
      url = url + '&lc=' + lc
      this.$u.router.push('webView', {
        url: url
      }).then(() => {})
      .catch((e) => {
        console.log(e)
      })
    },
    
    // 点击查看四柱留意触发事件
    relativeHandler () {
      // 获取信息
      this.getRelations()
      // 显示模态框
      this.$refs.relativeModal.show()
      // 滚到最上方
      this.modalScrollToTop()
    },
    
    // 点击全年节气触发事件
    termsHandler () {
      // 获取信息
      this.yearTerms = this.date.getYearTerms()
      // 显示
      this.$refs.termsModal.show()
      // 滚到最上方
      this.modalScrollToTop()
    },
    
    // 点击月相日历触发事件
    moonsHandler () {
      this.isMoonCalendar = !this.isMoonCalendar
    },
    
    // 关闭四柱留意触发事件
    closeRelativeHandler () {
      this.$refs.relativeModal.hide()
    },
    
    // 关闭全年节气触发事件
    closeTermsHandler () {
      this.$refs.termsModal.hide()
    },
    
    // 模态框滚动到顶
    modalScrollToTop () {
      // 滚到最上方
      // 为了填uni-app的坑，使得scrollTop能够变化，触发感应
      this.$nextTick(() => {
        this.modalScrollTop = 0
      })
      this.modalScrollTop = -1
    },
    
    // 设置日期
    setDate (year, month, day, hour = null, min = null) {
      if (this.isWeekCalendarShow) {
        if (hour !== null && min !== null) {
          this.$refs.weekCalendar.gotoTime(hour, min)
        }
        // 先调整时间，再调整日子
        this.$refs.weekCalendar.gotoDay(year, month, day)
      }
      else {
        if (hour !== null && min !== null) {
          this.$refs.monthCalendar.gotoTime(hour, min)
        }
        // 先调整时间，再调整日子
        this.$refs.monthCalendar.gotoDay(year, month, day)
      }
    },
    
    // 确定日期选择时的回调
    confirmHandler (type, values, text, indexes) {
      let options = {}
      if (type === 'lunar') {
        // 农历定时
        options = CM.Date.rulueToDate(values[2])
        this.setDate(options.year, options.month, options.day, values[3], values[4])
      }
      else {
        this.setDate(values[0], values[1], values[2], values[3], values[4])
      }
    },
    
    // 确定日期输入的回调
    inputConfirmHandler () {
      if (!this.$u.str.isNumberString(this.inputYear) || !this.$u.str.isNumberString(this.inputMonth) || !this.$u.str.isNumberString(this.inputDay)) {
        this.$u.msg.toast('不合法的输入')
        return
      }
      
      const year = parseInt(this.inputYear)
      const month = parseInt(this.inputMonth)
      const day = parseInt(this.inputDay)
      
      this.$refs.timeInput.hide()
      
      this.setDate(year, month, day)
    },
    
    // 导航发生变换触发事件
    navSelectHandler (item, index) {
      const pos = this.posList[index]
      if (pos) {
        // 除了顶部元素，下方的其他元素+5像素，增加容错率
        const scroll = pos.start === 0 ? 0 : pos.start + 5
        uni.pageScrollTo({
          duration: SCROLL_DURATION,
          scrollTop: scroll
        })
        if (!this.navScrollLock) {
          this.navScrollLock = true
          this.wait(SCROLL_DURATION * 3, 'calendar-nav').then(() => {
            this.navScrollLock = false
          })
          .catch((e) => {
            console.log(e)
          })
        }
      }
    },
    
    // 选中调整的干支
    ganzhiSelectionChangeHandler (index) {
      this.ganzhiSelectedIndex = index
    },
    
    // 时间偏移干支
    dateOffsetHandler: utils.common.throttle(function (offset = 1) {
      if (this.ganzhiSelectedIndex === 3) {
        // 调整时间，一次调整两个小时
        const hourInDay = CM.Date.config.HOUR_IN_DAY
        let desRulue = this.date.rulue
        let desHour = this.date.hour + offset
        while (desHour < 0) {
          desHour = desHour + hourInDay
          desRulue--
        }
        while (desHour >= hourInDay) {
          desHour = desHour - hourInDay
          desRulue++
        }
        const dayOptions = CM.Date.rulueToDate(desRulue)
        // 设置日期
        this.setDate(dayOptions.year, dayOptions.month, dayOptions.day, desHour, this.date.min)
      }
      else {
        const dayRulueOffset = 1
        const monthRulueOffset = 30
        const yearRulueOffset = 365
        let rulueOffset = 0
        if (this.ganzhiSelectedIndex === 2) {
          rulueOffset = offset * dayRulueOffset
        }
        else if (this.ganzhiSelectedIndex === 1) {
          rulueOffset = offset * monthRulueOffset
        }
        else if (this.ganzhiSelectedIndex === 0) {
          rulueOffset = offset * yearRulueOffset
        }
        let desRulue = this.date.rulue + rulueOffset
        const dayOptions = CM.Date.rulueToDate(desRulue)
        // 设置日期
        this.setDate(dayOptions.year, dayOptions.month, dayOptions.day, this.date.hour, this.date.min)
      }
    }, CALENDAR_DURATION),  
    
    // 选中时辰触发
    hourClickHandler: utils.common.throttle(function (index) {
      const hourInDay = CM.Date.config.HOUR_IN_DAY
      const hourStart = (index * 2 + 23) % 24
      const desHour = (hourStart + 1) % hourInDay
      this.setDate(this.date.year, this.date.month, this.date.day, desHour, this.date.min)
    }, CALENDAR_DURATION),
    
    // 选中月相触发
    moonClickHandler: utils.common.throttle(function (item) {
      this.setDate(item.year, item.month, item.day, this.date.hour, this.date.min)
    }, CALENDAR_DURATION),
    
    // 导航条吸顶触发
    stickyHandler (isFixed) {
      if (isFixed) {
        this.isWeekCalendarShow = true
      }
      else {
        this.isWeekCalendarShow = false
      }
    },
    
    // 导航条同步
    navSync (top) {
      const len = this.posList.length
      for (let i = 0; i < len; i++) {
        if (top >= this.posList[i].start) {
          if (this.posList[i + 1] && top < this.posList[i + 1].start || i + 1 === len) {
            // 导航条滑动过去
            this.$refs.scrollBar.scrollToIndex(i)
          }
        }
      }
    },
    
    // 计算八字符号之间的关系
    getRelations () {
      this.relations = {}
      
      const ganRelationObjs = []
      const zhiRelationObjs = []
      // 只排四柱,忽略刻柱
      const len = this.date.ganzhi.length > 4 ? 4 : this.date.ganzhi.length
      for (let i = 0; i < len; i++) {
        ganRelationObjs.push(this.date.ganzhi[i].gan)
        zhiRelationObjs.push(this.date.ganzhi[i].zhi)
      }
      
      // 定义方法列表
      // 天干看相合 相冲
      const ganMethods = ['ganHe', 'ganChong']
      // 地支看刑冲破害合会
      const zhiMethods = ['xing', 'zhiChong', 'po', 'hai', 'zhiHe', 'zhiThreeHe', 'zhiHui']
      
      this.relations.gan = new CM.Relation(ganRelationObjs, ganMethods)
      this.relations.zhi = new CM.Relation(zhiRelationObjs, zhiMethods)
    },
    
    // 初始化导航滚动内容的高度
    initPos () {
      if (!this.posList) {
        this.posList = []
      }
      
      // 初始滚动高度
      let top = 0
      // fixed部分的高度
      let fixedTop = CALENDAR_LINE_HEIGHT 
          + CALENDAR_WEEK_HEIGHT 
          + CALENDAR_PADDING
          + 2 * CALENDAR_BORDER
          + SCROLL_BAR_HEIGHT
      
      const len = this.navData.length
      for (let i = 0; i < len; i++) {
        const id = 'content-' + this.navData[i].id
        let item = uni.createSelectorQuery().select('#' + id)
        item.boundingClientRect((data) => {          
          let obj = {}
          if (i === 0) {
            // 月历在顶部，所以月历的顶部为参照初始高度
            top = data.top
            obj.start = 0
            // 触发吸顶的高度，为月历的结束
            obj.end = this.scrollBarFixedHeight
          }
          else {
            // 其他组件还要减去吸顶的滑动高度
            obj = {
              start: data.top - top - fixedTop,
              end: data.bottom - top - fixedTop
            }            
          }
          // console.log(data, obj)
          this.posList.push(obj)
        }).exec()
      }
    },
    
    // 初始化日出日落组件，画半圆
    initSun () {
      // 左右各留出20px
      const PADDING = 20
      let query = uni.createSelectorQuery().select('.sun-baseline')
      query.boundingClientRect((data) => {
        let width = data.width - 2 * PADDING
        // 宽度不要超出360
        width = width > 360 ? 360 : width
        const height = width * (1 / 2)
        this.$set(this.sunStyle, 'width', width + 'px')
        this.$set(this.sunStyle, 'height', height + 'px')
        this.$set(this.sunStyle, 'borderRadius', `${width}px ${width}px 0 0`)
        const delta = (data.width - width) / 2
        this.$set(this.sunDetailStyle, 'padding', `0 ${delta}px`)
      }).exec()
    },
    
    // 给定时间，获取字符串
    timeStr (timeObj) {
      return CM.Date.transDateStr(timeObj.year, timeObj.month, timeObj.day, timeObj.hour, timeObj.min)
    },
    
    // 节气对应的颜色
    termColor (index) {
      const colorList = [
        'sea', 'grey', 'background',
        'grass', 'sky', 'forest', 'grass', 'sky', 'forest',
        'red', 'pink', 'mauve', 'red', 'pink', 'mauve',
        'yellow', 'orange', 'brown', 'yellow', 'orange', 'brown',
        'sea', 'grey', 'background'
      ]
      return colorList[index]
    },
    
    // 月相对应颜色
    moonColor (index) {
      const colorList = [
        'purple', 'sea', 'yellow', 'forest'
      ]
      return colorList[index]
    },
    
    // 月相对应图标
    moonIcon (index, offset) {
      offset = offset > 6 ? 6 : offset
      // 所有月相图标
      const iconList = [
        ['wi-moon-new', 'wi-moon-waxing-crescent-1', 'wi-moon-waxing-crescent-2', 'wi-moon-waxing-crescent-3', 'wi-moon-waxing-crescent-4', 'wi-moon-waxing-crescent-5', 'wi-moon-waxing-crescent-6'],
        ['wi-moon-first-quarter', 'wi-moon-waxing-gibbous-1', 'wi-moon-waxing-gibbous-2', 'wi-moon-waxing-gibbous-3', 'wi-moon-waxing-gibbous-4', 'wi-moon-waxing-gibbous-5', 'wi-moon-waxing-gibbous-6'],
        ['wi-moon-full', 'wi-moon-waning-gibbous-1', 'wi-moon-waning-gibbous-2', 'wi-moon-waning-gibbous-3', 'wi-moon-waning-gibbous-4', 'wi-moon-waning-gibbous-5', 'wi-moon-waning-gibbous-6'],
        ['wi-moon-third-quarter', 'wi-moon-waning-crescent-1', 'wi-moon-waning-crescent-2', 'wi-moon-waning-crescent-3', 'wi-moon-waning-crescent-4', 'wi-moon-waning-crescent-5', 'wi-moon-waning-crescent-6']
      ]
      return 'iconfont icon-' + iconList[index][offset]
    },
  },
  // pageScroll的粒度有限，所以难以实现更加酷炫的过渡效果，先这样
  onPageScroll (data) {
    const top = data.scrollTop
    
    // 触发导航条同步
    if (!this.navScrollLock) {
      this.navSync(top)
    }
  },
  onLoad (data) {
    // console.log(data)
  }
}